﻿using GameFramework.Battle.Tools;
using LogSystem;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Gameplay.PVE.Survivor;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using UnityEngine.AssetStandardProcessors;
using UnityEngine.Rendering;
using Yoozoo.Gameplay.RTS;
using Object = UnityEngine.Object;

namespace Yoozoo.Gameplay.City.GpuSkinning.Editor
{
    public class GpuSkinningAnimatorCreator
    {
        private static string configAssetPath = "Assets/Scripts/Gameplay/City/GpuSkinning/Editor/cityNpcConfig.asset";
        private static string prefabDirectory = "Assets/ResourcesAssets/City/GpuSkinning";
        private static string textureDataDirectory = "Assets/BinaryAssets/City/GpuSkinning";
        private static string animationDirectory = "Assets/Arts/Character/Role/City/GpuSkinning/Common/Animations";
        private static string templateModelPath = "Assets/Arts/Character/Role/City/RTS300/Beauty_model_10001_RTS/Models/mesh_Beauty_model_10001_RTS.fbx";
        private static string assetRootPath = "Assets/Arts/Character/Role/City/GpuSkinning/";
        private string assetPath;
        private string modelAssetName;
        private string prefabAssetName;
        private string assetDirectory;
        private string materialDirectory;
        private string meshDirectory;
        private int index;
        
        private static string cityConfigAssetPath = "Assets/Scripts/Gameplay/City/GpuSkinning/Editor/cityNpcConfig.asset";
        private static string cityPrefabDirectory = "Assets/ResourcesAssets/City/GpuSkinning";
        private static string cityTextureDataDirectory = "Assets/BinaryAssets/City/GpuSkinning";
        private static string cityAnimationDirectory = "Assets/Arts/Character/Role/City/GpuSkinning/Common/Animations";
        private static string cityModelPath = "Assets/Arts/Character/Role/City/RTS300/visitor/shejiaomingyuan_RTS/Moderls/mesh_shejiaomingyuan_RTS.fbx";
        private static string cityAssetRootPath = "Assets/Arts/Character/Role/City/GpuSkinning/";
        
        private static string survivorConfigAssetPath = "Assets/Scripts/Gameplay/PVE/Survivor/Editor/SurvivorModelConfig.asset";
        private static string survivorPrefabDirectory = "Assets/ResourcesAssets/Pve/Survivor/Prefabs/GpuSkinningModel";
        private static string survivorTextureDataDirectory = "Assets/BinaryAssets/Survivor/GpuSkinning";
        private static string survivorAnimationDirectory = "Assets/Arts/Character/Role/Survivor/GpuSkinning/Common/Animations";
        private static string survivorModelPath = "Assets/Arts/Character/Role/soldier/Enemy_Police_Bo/RPG/Models/mesh_ThinPolice_M_Mocap.fbx";
        private static string survivorAssetRootPath = "Assets/Arts/Character/Role/Survivor/GpuSkinning/";
        public static bool isSurvivor = false;
        
        private static Dictionary<int,Mesh> meshMap = new Dictionary<int, Mesh>();
        
        private GpuSkinningAnimatorCreator()
        {
            index = 1;
        }

        private static void PreprocessData()
        {
            if (isSurvivor)
            {
                configAssetPath = survivorConfigAssetPath;
                prefabDirectory = survivorPrefabDirectory;
                textureDataDirectory = survivorTextureDataDirectory;
                animationDirectory = survivorAnimationDirectory;
                templateModelPath = survivorModelPath;
                assetRootPath = survivorAssetRootPath;
            }
            else
            {
                configAssetPath = cityConfigAssetPath;
                prefabDirectory = cityPrefabDirectory;
                textureDataDirectory = cityTextureDataDirectory;
                animationDirectory = cityAnimationDirectory;
                templateModelPath = cityModelPath;
                assetRootPath = cityAssetRootPath;
            }
        }
        
        [MenuItem("GTATools/GamePlay/InnerCity/内城小人GPU Skinning使用说明")]
        private static void ShowHelpUrl()
        {
            Application.OpenURL("https://youzu.feishu.cn/docx/TvvudzbxPozy4exWbpkc53YKnwc");
        }

        //[MenuItem("GTATools/俯视角射击/收集小人信息")]
        public static void CollectSurvivorPrefabInfo()
        {
            isSurvivor = true;
            CollectPrefabInfoInPath("Assets/ResourcesAssets/Pve/Survivor/Prefabs/OriginModel",survivorConfigAssetPath);
            isSurvivor = false;
        }
        
        // 收集信息

        [MenuItem("GTATools/GamePlay/InnerCity/收集内城小人的信息")]
        private static void CollectPrefabInfo()
        {
            CollectPrefabInfoInPath("Assets/ResourcesAssets/City/Npc",cityConfigAssetPath);
        }

        private static void CollectPrefabInfoInPath(string inputPath,string outputPath)
        {
            var config = ScriptableObject.CreateInstance<CityNpcPrefabConfig>();

            string aiPath = inputPath;
            var guids = AssetDatabase.FindAssets("t:Prefab", new[] { aiPath });
            foreach (var guid in guids)
            {
                string assetPath = AssetDatabase.GUIDToAssetPath(guid);
                var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
                if (!prefab)
                {
                    Debug.LogErrorFormat(LogModule.City, "预制体{0}加载失败", assetPath);
                    continue;
                }

                var cityNpcInfo = new CityNpcInfo();
                cityNpcInfo.PrefabAssetPath = assetPath;

                // 1. 确定fbx
                var smrs = prefab.GetComponentsInChildren<SkinnedMeshRenderer>();
                SkinnedMeshRenderer skinningMeshRenderer = null;
                var gpuSkinningWeapon = prefab.GetComponent<GpuSkinningWeapon>();
                for (int i = 0; i < smrs.Length; i++)
                {
                    if (gpuSkinningWeapon != null && gpuSkinningWeapon.Contains(smrs[i].rootBone))
                    {
                        /*var mat = smrs[i].sharedMaterial;
                        if (mat.HasProperty("_BaseColorTex"))
                        {
                            var baseColorTex = mat.GetTexture("_BaseColorTex");
                            cityNpcInfo.WeaponTextureAssetPath = AssetDatabase.GetAssetPath(baseColorTex);
                        }
                        else if (mat.HasProperty("_MainTex"))
                        {
                            var mainTex = mat.GetTexture("_MainTex");
                            cityNpcInfo.WeaponTextureAssetPath = AssetDatabase.GetAssetPath(mainTex);
                        }
                        cityNpcInfo.WeaponMaterialAssetPath = AssetDatabase.GetAssetPath(mat);*/
                        continue;
                    }
                    skinningMeshRenderer = smrs[i];
                    break;
                }
                if (!skinningMeshRenderer)
                {
                    Debug.LogError(LogModule.City, $"预制体{assetPath}没有找到SkinnedMeshRenderer", prefab);
                    continue;
                }

                
                var mesh = skinningMeshRenderer.sharedMesh;
                if (!mesh)
                {
                    Debug.LogError(LogModule.City,
                        $"预制体{assetPath}的SkinnedMeshRenderer({skinningMeshRenderer.name})上的网格掉了", prefab);
                    continue;
                }

                cityNpcInfo.ModelAssetPath = AssetDatabase.GetAssetPath(mesh);

                var material = skinningMeshRenderer.sharedMaterial;
                if (!material)
                {
                    Debug.LogError(LogModule.City,
                        $"预制体{assetPath}的SkinnedMeshRenderer({skinningMeshRenderer.name})上的材质掉了", prefab);
                    continue;
                }

                cityNpcInfo.MaterialAssetPath = AssetDatabase.GetAssetPath(material);

                if (material.HasProperty("_BaseColorTex"))
                {
                    var baseColorTex = material.GetTexture("_BaseColorTex");
                    cityNpcInfo.MainTextureAssetPath = AssetDatabase.GetAssetPath(baseColorTex);
                }
                else if (material.HasProperty("_MainTex"))
                {
                    var mainTex = material.GetTexture("_MainTex");
                    cityNpcInfo.MainTextureAssetPath = AssetDatabase.GetAssetPath(mainTex);
                }
                else
                {
                    Debug.LogError(LogModule.City, $"预制体{assetPath}没有找到MainTex", prefab);
                    continue;
                }

                // 2. 确定controller
                var animator = prefab.GetComponentInChildren<Animator>();
                if (animator == null)
                {
                    Debug.LogError(LogModule.City, $"预制体{assetPath}没有找到Animator", prefab);
                    continue;
                }

                if (animator.runtimeAnimatorController == null)
                {
                    Debug.LogError(LogModule.City, $"预制体{assetPath}的Animator({animator.name})上的Controller掉了", prefab);
                    continue;
                }

                cityNpcInfo.AnimatorControllerPath = AssetDatabase.GetAssetPath(animator.runtimeAnimatorController);

                // 3. 确定scale & layer
                cityNpcInfo.Scale = GetScale(skinningMeshRenderer.transform, prefab.transform);
                cityNpcInfo.Layer = prefab.layer;

                // 4. Prefab名字
                cityNpcInfo.AssetName = Path.GetFileNameWithoutExtension(assetPath);

                // 5. 寻找weapon
                if (isSurvivor)
                {
                    if (gpuSkinningWeapon != null)
                    {
                        for (int i = 0; i < gpuSkinningWeapon.weaponList.Count; i++)
                        {
                            var renderer = gpuSkinningWeapon.weaponList[i].weapon;
                            if (renderer != null)
                            {
                                var mat = renderer.sharedMaterial;
                                if (mat.HasProperty("_BaseColorTex"))
                                {
                                    var baseColorTex = mat.GetTexture("_BaseColorTex");
                                    cityNpcInfo.meshTextureAssetPaths.Add(AssetDatabase.GetAssetPath(baseColorTex));
                                }
                                else if (mat.HasProperty("_MainTex"))
                                {
                                    var mainTex = mat.GetTexture("_MainTex");
                                    cityNpcInfo.meshTextureAssetPaths.Add(AssetDatabase.GetAssetPath(mainTex));
                                }
                                cityNpcInfo.meshMaterialAssetPaths.Add(AssetDatabase.GetAssetPath(mat));
                            }
                        }
                    }
                }
                config.NpcInfos.Add(cityNpcInfo);
            }

            if (File.Exists(outputPath))
                AssetDatabase.DeleteAsset(outputPath);
            AssetDatabase.CreateAsset(config, outputPath);

            if (!isSurvivor)
            {
                EditorUtility.DisplayDialog("收集内城小人的信息", "收集完成！\n保存到" + outputPath, "OK");
                var asset = AssetDatabase.LoadAssetAtPath<ScriptableObject>(outputPath);
                if (asset)
                    EditorGUIUtility.PingObject(asset);
            }
        }

        private static Vector3 GetScale(Transform current, Transform root)
        {
            if(current == null)
                return Vector3.one;

            if (current == root)
                return current.localScale;

            return Vector3.Scale(current.localScale, GetScale(current.parent, root));
        }

        public static void ExportSingle(string name)
        {
            meshMap.Clear();
            isSurvivor = true;
            PreprocessData();
            try
            {
                AssetGraphPostprocessor.Postprocessor.Enabled = false;
                AssetGraphPostprocessor.Postprocessor.Log = false;
                // 清空导出目录
                ClearDirectory(prefabDirectory);
                var creator = new GpuSkinningAnimatorCreator();
                var config = AssetDatabase.LoadAssetAtPath<CityNpcPrefabConfig>(configAssetPath);
                if (config == null)
                    return;
                foreach (var cityNpcInfo in config.NpcInfos)
                {
                    if (cityNpcInfo.PrefabAssetPath.Contains(name))
                    {
                        creator.SetPath(cityNpcInfo);
                        creator.MakeBoneMap(cityNpcInfo);
                        creator.PrintBoneList();
                        creator.ExportTex(cityNpcInfo.PrefabAssetPath,cityNpcInfo.AssetName);
                        creator.Export(cityNpcInfo);
                        break;
                    }
                }
                AssetDatabase.SaveAssets();
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogException(e);
            }
            finally
            {
                Debug.Log(stringBuilder);
                AssetGraphPostprocessor.Postprocessor.Enabled = true;
            }
           
        }
        
        [MenuItem("GTATools/俯视角射击/导出GPU Skinning")]
        static void ExportSurvivor()
        {
            CollectSurvivorPrefabInfo();
            var startTime = Environment.TickCount;
            meshMap.Clear();
            isSurvivor = true;
            PreprocessData();
            try
            {
                var config = AssetDatabase.LoadAssetAtPath<CityNpcPrefabConfig>(configAssetPath);
                if (config == null)
                    return;
                AssetGraphPostprocessor.Postprocessor.Enabled = false;
                AssetGraphPostprocessor.Postprocessor.Log = false;

                // 清空导出目录
                ClearDirectory(prefabDirectory);

                var creator = new GpuSkinningAnimatorCreator();

                foreach (var cityNpcInfo in config.NpcInfos)
                {
                    creator.SetPath(cityNpcInfo);
                    creator.MakeBoneMap(cityNpcInfo);
                    creator.PrintBoneList();
                    creator.ExportTex(cityNpcInfo.PrefabAssetPath,cityNpcInfo.AssetName);
                    creator.Export(cityNpcInfo);
                }
                AssetDatabase.SaveAssets();
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogException(e);
            }
            finally
            {
                Debug.Log(stringBuilder);
                AssetGraphPostprocessor.Postprocessor.Enabled = true;
                var diff = Environment.TickCount - startTime;
                BattleDebug.LogError("导出总耗时 " + diff / 1000f + "s");
            }
        }
        
        [MenuItem("GTATools/GamePlay/InnerCity/批量创建内城小人GPU Skinning")]
        static void ExportByConfig()
        {
            isSurvivor = false;
            PreprocessData();
            var config = AssetDatabase.LoadAssetAtPath<CityNpcPrefabConfig>(configAssetPath);
            if (config == null)
                return;

            try
            {
                AssetGraphPostprocessor.Postprocessor.Enabled = false;
                AssetGraphPostprocessor.Postprocessor.Log = false;

                // 清空导出目录
                ClearDirectory(prefabDirectory);

                var creator = new GpuSkinningAnimatorCreator();

                foreach (var cityNpcInfo in config.NpcInfos)
                {
                    creator.MakeBoneMap(cityNpcInfo);
                }
                creator.PrintBoneList();

                // 找一个模型，生成一张图
                creator.ExportTex(templateModelPath);

                foreach (var cityNpcInfo in config.NpcInfos)
                {
                    creator.SetPath(cityNpcInfo);
                    creator.Export(cityNpcInfo);
                }

                AssetDatabase.SaveAssets();
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogException(e);
            }
            finally
            {
                Debug.Log(stringBuilder);
                AssetGraphPostprocessor.Postprocessor.Enabled = true;
            }
        }

        private void ExportTex(string templateModelPath,string assetName = "Common")
        {
            var modelPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(templateModelPath);
            var obj = Object.Instantiate(modelPrefab);
            var smrs = obj.GetComponentsInChildren<SkinnedMeshRenderer>();
            SkinnedMeshRenderer skinnedMeshRenderer = null;
            for (int i = 0; i < smrs.Length; i++)
            {
                if (IsInvalidRenderer(smrs[i]))
                {
                    continue;
                }
                skinnedMeshRenderer = smrs[i];
                break;
            }
            var boneTransformList = CollectBones(obj, new List<SkinnedMeshRenderer> { skinnedMeshRenderer });

            var generator = new GPUInstancedAnimationGenerator(this);
            generator.m_bakeFrame = 20;

            //animationDirectory = "Assets/Arts/Character/Role/City/GpuSkinning/Common/Animations";
            //textureDataDirectory = "Assets/Arts/Character/Role/City/GpuSkinning/Common/Textures";
            if (!Directory.Exists(animationDirectory))
                Directory.CreateDirectory(animationDirectory);
            if (!Directory.Exists(textureDataDirectory))
                Directory.CreateDirectory(textureDataDirectory);

            prefabAssetName = assetName;
            generator.ToGenerate2(animationDirectory, textureDataDirectory, prefabDirectory, prefabAssetName, obj, skinnedMeshRenderer, boneTransformList, animationClipList);

            Object.DestroyImmediate(obj);
        }

        /// <summary>
        /// 骨骼名到编号的映射表
        /// </summary>
        private Dictionary<string, int> boneMap = new Dictionary<string, int>();

        private List<string> boneNameList = new List<string>();

        private Dictionary<AnimationClip, int> animationClipMap = new Dictionary<AnimationClip, int>();
        
        private Dictionary<AnimationClip, int> animationOverrideClipMap = new Dictionary<AnimationClip, int>();

        private List<AnimationClip> animationClipList = new List<AnimationClip>();

        public static bool IsInvalidRenderer(SkinnedMeshRenderer renderer)
        {
            if (!isSurvivor)
            {
                return false;
            }

            if (renderer == null)
            {
                return true;
            }

            var isInvalidBone = IsInvalidBone(renderer.rootBone);
            if (!isInvalidBone)
            {
                return renderer.name.ToLower().Contains("hair");
            }
            return true;
        }
        public static bool IsInvalidBone(Transform transform)
        {
            if (!isSurvivor)
            {
                return false;
            }
            if (transform == null)
            {
                return false;
            }
            return transform.name.Contains("Weapon") || transform.name.Contains("Prop1");
        }
        
        private void MakeBoneMap(CityNpcInfo cityNpcInfo)
        {
            if (isSurvivor)
            {
                boneMap.Clear();
                boneNameList.Clear();
                animationClipList.Clear();
                animationClipMap.Clear();
                animationOverrideClipMap.Clear();
            }
            var obj = AssetDatabase.LoadAssetAtPath<GameObject>(cityNpcInfo.ModelAssetPath);

            //根节点要相同
            var skinnedMeshRenderers = obj.GetComponentsInChildren<SkinnedMeshRenderer>();
            var validSkinnedMeshRenderers = new List<SkinnedMeshRenderer>();

            string rootBoneName = null;
            for (int i = 0; i < skinnedMeshRenderers.Length; i++)
            {
                // 对摩托兵的特殊处理，这几个节点没有使用，手动排除掉
                var skinnedMeshRenderer = skinnedMeshRenderers[i];
                if (skinnedMeshRenderer.name == "Mash_motorbike_01_RTS")
                {
                    continue;
                }

                if (skinnedMeshRenderer.name == "SOLD411021_Smg_Weapon_RTS")
                {
                    continue;
                }
                if (isSurvivor)
                {
                    if (skinnedMeshRenderer.rootBone == null)
                    {
                        continue;
                    }
                    if (IsInvalidRenderer(skinnedMeshRenderer))
                    {
                        continue;
                    }
                    else
                    {
                        if (rootBoneName == null)
                        {
                            rootBoneName = skinnedMeshRenderer.rootBone.name;
                        }
                    }
                }
                else
                {
                    if (rootBoneName == null)
                    {
                        rootBoneName = skinnedMeshRenderer.rootBone.name;
                    }
                    else if (!rootBoneName.Equals(skinnedMeshRenderer.rootBone.name))
                    {
                        Debug.LogError(LogModule.City, $"预制体{assetPath}有{skinnedMeshRenderers.Length}个蒙皮，但是rootBone不相同 {rootBoneName} != {skinnedMeshRenderer.rootBone.name}", obj);
                        return;
                    }
                }
                validSkinnedMeshRenderers.Add(skinnedMeshRenderer);
            }


            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("model: {0}\n", cityNpcInfo.AssetName);
            foreach (var skinnedMeshRenderer in validSkinnedMeshRenderers)
            {
                sb.AppendFormat("SkinnedMeshRenderer: {0}\n", skinnedMeshRenderer.name);
                var bones = skinnedMeshRenderer.bones;
                sb.AppendFormat("bones: {0}\n", bones.Length);
                for (var i = 0; i < bones.Length; i++)
                {
                    var bone = bones[i];
                    var boneName = bone.name;

                    // 不要包含手指和脚趾
                    if (IsIgnoredBone(boneName))
                    {
                        sb.AppendFormat("包含: {0}\n", boneName);
                        continue;
                    }

                    sb.AppendFormat("[{0}]: {1}\n", i + 1, boneName);
                    if (!boneMap.ContainsKey(boneName))
                    {
                        boneMap[boneName] = boneMap.Count;
                        boneNameList.Add(boneName);
                    }
                }
            }

            if (isSurvivor)
            {
                var originPrefab = obj = AssetDatabase.LoadAssetAtPath<GameObject>(cityNpcInfo.PrefabAssetPath);
                var weapon = originPrefab.GetComponent<GpuSkinningWeapon>();
                if (weapon != null)
                {
                    for (int i = 0; i < weapon.weaponList.Count; i++)
                    {
                        var boneName = weapon.weaponList[i].point.name;
                        sb.AppendFormat("[{0}]: {1}\n", boneNameList.Count + 1, boneName);
                        if (!boneMap.ContainsKey(boneName))
                        {
                            boneMap[boneName] = boneMap.Count;
                            boneNameList.Add(boneName);
                        }
                    }
                }
            }

            var animatorController = AssetDatabase.LoadAssetAtPath<AnimatorController>(cityNpcInfo.AnimatorControllerPath);
            if (animatorController != null)
            {
                var animationClips = animatorController.animationClips;
                sb.AppendFormat("animationClips: {0}\n", animationClips.Length);
                for (var i = 0; i < animationClips.Length; i++)
                {
                    var animationClip = animationClips[i];
                    sb.AppendFormat("[{0}]: {1}\n", i + 1, animationClip.name);
                    if (!animationClipMap.ContainsKey(animationClip))
                    {
                        animationClipMap[animationClip] = animationClipMap.Count;
                        animationClipList.Add(animationClip);
                    }
                }
            }
            else
            {
                var aoc = AssetDatabase.LoadAssetAtPath<AnimatorOverrideController>(cityNpcInfo.AnimatorControllerPath);
                var overrides = new List<KeyValuePair<AnimationClip, AnimationClip>>(aoc.overridesCount);
                aoc.GetOverrides(overrides);
                for (int k = 0; k < overrides.Count; k++)
                {
                    var originClip = overrides[k].Key;
                    var targetClip = overrides[k].Value;
                    if (targetClip == null)
                    {
                        continue;
                    }
                    if (!animationClipMap.ContainsKey(targetClip))
                    {
                        animationClipMap[targetClip] = animationClipMap.Count;
                        animationClipList.Add(targetClip);
                    }

                    if (!animationOverrideClipMap.ContainsKey(originClip))
                    {
                        animationOverrideClipMap[originClip] = animationClipMap[targetClip];
                    }
                }
            }
            Debug.Log(sb);
        }

        private bool IsIgnoredBone(string boneName)
        {
            if (isSurvivor)
            {
                return false;
            }
            if (boneName.Contains("Finger0"))
                return false;

            // 不要包含手指和脚趾
            if (boneName.Contains("Finger") || boneName.Contains("Toe"))
                return true;

            return false;
        }

        private void PrintBoneList()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("Bones: {0}\n", boneNameList.Count);
            for (var i = 0; i < boneNameList.Count; i++)
            {
                var boneName = boneNameList[i];
                sb.AppendFormat("[{0}]: {1}\n", i, boneName);
            }

            sb.AppendFormat("animationClips: {0}\n", animationClipList.Count);
            for (var i = 0; i < animationClipList.Count; i++)
            {
                var animationClip = animationClipList[i];
                sb.AppendFormat("[{0}]: {1}\n", i, animationClip.name);
            }
            UnityEngine.Debug.LogError(sb);
        }

        private static void ClearDirectory(string directory)
        {
            if (Directory.Exists(directory))
            {
                var guids = AssetDatabase.FindAssets("t:Object", new[] { directory });
                foreach (var guid in guids)
                {
                    var path = AssetDatabase.GUIDToAssetPath(guid);
                    if (path.EndsWith(".png"))
                        continue;
                    AssetDatabase.DeleteAsset(path);
                }
            }
        }

        void SetPath(CityNpcInfo cityNpcInfo)
        {
            // Assets/Arts/Character/Role/City/HERO41001_Fu/xxx

            prefabAssetName = cityNpcInfo.AssetName;

            // Assets/Arts/Character/Role/hero/HERO41001_Fu/RTS/Models/mesh_HERO41001_Fu_RTS.FBX
            assetPath = cityNpcInfo.ModelAssetPath;

            // mesh_HERO41001_Fu_RTS
            modelAssetName = Path.GetFileNameWithoutExtension(assetPath);

            // Assets/Arts/Character/Role/hero/HERO41001_Fu/RTS/Models
            assetDirectory = Path.GetDirectoryName(assetPath);

            // Assets/Arts/Character/Role/City/GpuSkinning/CityNpc_Fu/
            assetDirectory = $"{assetRootPath}{cityNpcInfo.AssetName}/";
            ClearDirectory(assetDirectory);

            // Assets/Arts/Character/Role/City/GpuSkinning/CityNpc_Fu/Materials
            materialDirectory = Path.Combine(assetDirectory, "Materials");
            if (!Directory.Exists(materialDirectory))
                Directory.CreateDirectory(materialDirectory);

            // Assets/Arts/Character/Role/City/GpuSkinning/CityNpc_Fu/Meshes
            meshDirectory = Path.Combine(assetDirectory, "Meshes");
            if (!Directory.Exists(meshDirectory))
                Directory.CreateDirectory(meshDirectory);

            // Assets/Arts/Character/Role/City/GpuSkinning/CityNpc_Fu/Animations
            animationDirectory = Path.Combine(assetDirectory, "Animations");
            if (!Directory.Exists(animationDirectory))
                Directory.CreateDirectory(animationDirectory);

            if (!Directory.Exists(prefabDirectory))
                Directory.CreateDirectory(prefabDirectory);
            BattleDebug.LogError("正在处理 " + prefabAssetName);
        }

        void Export(CityNpcInfo cityNpcInfo)
        {
            ExportCharacterPrefab(cityNpcInfo);
        }
        
        //导出角色的Prefab
        private void ExportCharacterPrefab(CityNpcInfo cityNpcInfo)
        {
            var obj = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
            RuntimeAnimatorController animatorController = AssetDatabase.LoadAssetAtPath<AnimatorController>(cityNpcInfo.AnimatorControllerPath);
            if (animatorController == null)
            {
                animatorController = AssetDatabase.LoadAssetAtPath<AnimatorOverrideController>(cityNpcInfo.AnimatorControllerPath);
            }

            Texture mainTexture = null;
            if(!string.IsNullOrEmpty(cityNpcInfo.MainTextureAssetPath))
                mainTexture = AssetDatabase.LoadAssetAtPath<Texture>(cityNpcInfo.MainTextureAssetPath);
            
            //根节点要相同
            var skinnedMeshRenderers = obj.GetComponentsInChildren<SkinnedMeshRenderer>();
            var validSkinnedMeshRenderers = new List<SkinnedMeshRenderer>();

            var originPrefab = GameObject.Instantiate(AssetDatabase.LoadAssetAtPath<GameObject>(cityNpcInfo.PrefabAssetPath));
            var gpuSkinningWeapon = originPrefab.GetComponent<GpuSkinningWeapon>();
            string rootBoneName = null;
            for (int i = 0; i < skinnedMeshRenderers.Length; i++)
            {
                // 对摩托兵的特殊处理，这几个节点没有使用，手动排除掉
                var skinnedMeshRenderer = skinnedMeshRenderers[i];
                if (skinnedMeshRenderer.name == "Mash_motorbike_01_RTS")
                {
                    continue;
                }

                if (skinnedMeshRenderer.name == "SOLD411021_Smg_Weapon_RTS")
                {
                    continue;
                }

                if (IsInvalidRenderer(skinnedMeshRenderer))
                {
                    continue;
                }
                if (gpuSkinningWeapon != null && gpuSkinningWeapon.Contains(skinnedMeshRenderer.rootBone))
                {
                    continue;
                }
                if (rootBoneName == null)
                {
                    rootBoneName = skinnedMeshRenderer.rootBone.name;
                }
                else if (!rootBoneName.Equals(skinnedMeshRenderer.rootBone.name))
                {
                    Debug.LogError(LogModule.City, $"预制体{assetPath}有{skinnedMeshRenderers.Length}个蒙皮，但是rootBone不相同 {rootBoneName} != {skinnedMeshRenderer.rootBone.name}", obj);
                    return;
                }

                validSkinnedMeshRenderers.Add(skinnedMeshRenderer);
            }

            //记录fbx的Mesh，与新生成的Mesh
            var rebindPoseMeshes = new List<KeyValuePair<Mesh, Mesh>>();
            //获取所有的骨骼Transform
            var boneTransformList = CollectBones(obj, validSkinnedMeshRenderers);
            for (int i = 0; i < validSkinnedMeshRenderers.Count; i++)
            {
                var skinnedMeshRenderer = validSkinnedMeshRenderers[i];
                string meshName = skinnedMeshRenderer.sharedMesh.name;

                Mesh targetMesh;

                string meshPath = $"{meshDirectory}/mesh_{meshName}.asset";
                // 清理掉老的mesh.asset
                if (File.Exists(meshPath))
                {
                    AssetDatabase.DeleteAsset(meshPath);
                }
                
                var boneSource = skinnedMeshRenderer;
                //只允许有一个subMesh
                if (skinnedMeshRenderer.sharedMesh.subMeshCount > 1)
                {
                    UnityEngine.Debug.LogError("Not subMeshCount > 1:" + meshName);
                    continue;
                }

                var hashCode = skinnedMeshRenderer.sharedMesh.GetHashCode();
                if (isSurvivor && meshMap.TryGetValue(hashCode, out var mesh))
                {
                    targetMesh = mesh;
                    rebindPoseMeshes.Add(new KeyValuePair<Mesh, Mesh>(skinnedMeshRenderer.sharedMesh, targetMesh));
                    continue;
                }
                else
                {
                    targetMesh = Object.Instantiate(skinnedMeshRenderer.sharedMesh);
                    targetMesh.uv3 = null;
                    AssetDatabase.CreateAsset(targetMesh, meshPath);
                    meshMap[hashCode] = targetMesh;
                }
                RebuildMeshBindPose(obj, boneSource, targetMesh, boneTransformList);
                // readable mesh data

                string readableMeshPath = meshPath + ".obj";
                if(File.Exists(readableMeshPath))
                    File.Delete(readableMeshPath);

                using (StreamWriter streamWriter = new StreamWriter(File.Open(readableMeshPath, FileMode.Create)))
                {
                    streamWriter.WriteLine("mtllib {0}.mtl", modelAssetName);
                    streamWriter.WriteLine("# {0} vertices", targetMesh.vertices.Length);
                    foreach (var v in targetMesh.vertices)
                    {
                        streamWriter.WriteLine("v {0} {1} {2}", v.x, v.y, v.z);
                    }

                    streamWriter.WriteLine("# normals");
                    foreach (var v in targetMesh.normals)
                    {
                        streamWriter.WriteLine("vn {0} {1} {2}", v.x, v.y, v.z);
                    }

                    streamWriter.WriteLine("# bindposes");
                    foreach (var m in targetMesh.bindposes)
                    {
                        streamWriter.WriteLine("#bp {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14} {15}", 
                            m.m00, m.m01, m.m02, m.m03, m.m10, m.m11, m.m12, m.m13, m.m20, m.m21, m.m22, m.m23, m.m30, m.m31, m.m32, m.m33);
                    }

                    streamWriter.WriteLine("# uv0");
                    var uv0 = new List<Vector4>();
                    targetMesh.GetUVs(0, uv0);
                    foreach (var v in uv0)
                    {
                        streamWriter.WriteLine("#uv0 {0} {1} {2} {3}", v.x, v.y, v.z, v.w);
                    }

                    streamWriter.WriteLine("# uv1");
                    var uv1 = new List<Vector4>();
                    targetMesh.GetUVs(1, uv1);
                    foreach (var v in uv1)
                    {
                        streamWriter.WriteLine("#uv1 {0} {1} {2} {3}", v.x, v.y, v.z, v.w);
                    }

                    streamWriter.WriteLine("# uv2");
                    var uv2 = new List<Vector4>();
                    targetMesh.GetUVs(2, uv2);
                    foreach (var v in uv2)
                    {
                        streamWriter.WriteLine("#uv2 {0} {1} {2} {3}", v.x, v.y, v.z, v.w);
                    }

                    var indices = targetMesh.GetIndices(0);
                    int count = indices.Length / 3;
                    streamWriter.WriteLine("# {0} triangles", count);
                    for (int j = 0; j < count; j++)
                    {
                        streamWriter.WriteLine("f {0} {1} {2}", indices[j * 3 + 0] + 1, indices[j * 3 + 1] + 1, indices[j * 3 + 2] + 1);
                    }
                }

                EditorUtility.SetDirty(targetMesh);

                rebindPoseMeshes.Add(new KeyValuePair<Mesh, Mesh>(skinnedMeshRenderer.sharedMesh, targetMesh));
            }

            // 生成GPUSkinning信息
            var generator = new GPUInstancedAnimationGenerator(this);
            generator.m_bakeFrame = 20;
            generator.ToGenerate(animationDirectory, textureDataDirectory, prefabAssetName, obj, validSkinnedMeshRenderers, animatorController,boneTransformList,originPrefab);
            var gpuAnim = generator.m_allBoneAnimation;
            if (gpuAnim == null)
            {
                UnityEngine.Debug.LogError("GPU Anim error:" + prefabAssetName);
                return;
            }
            
            for (int i = 0; i < validSkinnedMeshRenderers.Count; i++)
            {
                var skinnedMeshRenderer = validSkinnedMeshRenderers[i];
                string rendererName = skinnedMeshRenderer.name;

                Mesh targetMesh = rebindPoseMeshes.Find(x => x.Key == skinnedMeshRenderer.sharedMesh).Value;

                string objName = cityNpcInfo.AssetName;
                string prefabPath = Path.Combine(prefabDirectory, objName + ".prefab");
                GameObject newPrefab = new GameObject(objName);

                var material = AssetDatabase.LoadAssetAtPath<Material>(cityNpcInfo.MaterialAssetPath);
                var shader = Shader.Find("GGame/HRP/MainCity/GPUSkinning");
                var shadowShader = Shader.Find("GGame/HRP/MainCity/GPUSkinningShadow");
                if (isSurvivor)
                {
                    shadowShader = Shader.Find("GGame/HRP/MainCity/GPUSkinningShadowRpg");
                }
                GetInstanceMaterial(rendererName, material, mainTexture, shader,shadowShader,out var instanceMat, out var shadowMat,true);

                var modelInstaning = newPrefab.AddComponent<ModelInstaning>();
                modelInstaning.mat = instanceMat;
                modelInstaning.shadowMat = shadowMat;
                modelInstaning.shadowMesh = targetMesh;
                modelInstaning.animationInfo = gpuAnim;
                modelInstaning.localScale = cityNpcInfo.Scale.x;
                modelInstaning.poolKey = index;
                index++;
                newPrefab.layer = cityNpcInfo.Layer;
                GameObject rootGameObject = null;
                ModelInstancingGroup instancingGroup = null;
                GameObject prefab = null;
                if (isSurvivor)
                {
                    rootGameObject = new GameObject();
                    instancingGroup = rootGameObject.AddComponent<ModelInstancingGroup>();
                    string allAnimationAssetPath = Path.Combine(prefabDirectory, cityNpcInfo.AssetName) + ".asset";
                    var animationInfo = AssetDatabase.LoadAssetAtPath<GPUSkinning_AllAnimation>(allAnimationAssetPath);
                    instancingGroup.animationInfo = animationInfo;
                    rootGameObject.name = objName;
                    newPrefab.transform.SetParent(rootGameObject.transform);
                    instancingGroup.modelInstancing = modelInstaning;
                }
                if (gpuSkinningWeapon != null)
                {
                    var hasGun = false;
                    for (int j = 0; j < gpuSkinningWeapon.weaponList.Count; j++)
                    {
                        var weapon = gpuSkinningWeapon.weaponList[j];
                        Mesh meshAsset = null;
                        var isWeapon = false;
                        if (weapon.weapon != null)
                        {
                            if (weapon.weapon is SkinnedMeshRenderer mr)
                            {
                                isWeapon = true;
                                meshAsset = mr.sharedMesh;
                            }
                            else
                            {
                                var meshFilter = weapon.weapon.GetComponent<MeshFilter>();
                                if (meshFilter != null)
                                {
                                    meshAsset = meshFilter.sharedMesh;
                                }
                            }

                            if (meshAsset == null)
                            {
                                UnityEngine.Debug.LogError("meshAsset is null:" + weapon.weapon.name);
                            }
                            var gpuMesh = CreateGpuInstancing(weapon.weapon.name,cityNpcInfo.meshTextureAssetPaths[j],cityNpcInfo.meshMaterialAssetPaths[j],meshAsset);
                            //去原始的prefab里找枪口
                            for (int k = 0; k < weapon.keyPoints.Count; k++)
                            {
                                var keyPoint = weapon.keyPoints[k];
                                gpuMesh.gunPoint = new GameObject(keyPoint.name).transform;
                                gpuMesh.gunPoint.parent = modelInstaning.transform;
                                gpuMesh.gunPoint.localPosition = keyPoint.localPosition;
                                gpuMesh.gunPoint.localRotation = keyPoint.localRotation;
                                gpuMesh.gunPoint.localScale = keyPoint.localScale;
                                hasGun = true;
                            }
                            //找到绑定点位
                            for (int k = 0; k < gpuAnim.mountPoints.Length; k++)
                            {
                                if (gpuAnim.mountPoints[k] == weapon.point.name)
                                {
                                    gpuMesh.mountPointIndex = k;
                                    break;
                                }
                            }
                            gpuMesh.transform.SetParent(rootGameObject.transform);
                            //如果是武器，则直接选用原始位置
                            weapon.weapon.transform.SetParent(weapon.point.transform);

                            //if (isWeapon)
                            {
                                gpuMesh.transform.position = weapon.weapon.transform.localPosition;
                                gpuMesh.transform.rotation = weapon.weapon.transform.localRotation;
                                gpuMesh.transform.localScale = weapon.weapon.transform.lossyScale;
                            }
                            /*else
                            {
                                gpuMesh.transform.position = weapon.weapon.transform.localPosition;
                                gpuMesh.transform.rotation = weapon.weapon.transform.localRotation;
                                gpuMesh.transform.localScale = weapon.weapon.transform.lossyScale;
                            }*/
                            gpuMesh.gameObject.layer = cityNpcInfo.Layer;
                            //GameObject.DestroyImmediate(go);
                            //GameObject.DestroyImmediate(point);
                            instancingGroup.modelGpuInstancingList.Add(gpuMesh);
                        }
                        else
                        {
                            var index = 0;
                            Transform gunPoint = null;
                            //去原始的prefab里找枪口
                            for (int k = 0; k < weapon.keyPoints.Count; k++)
                            {
                                var keyPoint = weapon.keyPoints[k];
                                gunPoint = new GameObject(keyPoint.name).transform;
                                gunPoint.parent = modelInstaning.transform;
                                gunPoint.localPosition = keyPoint.localPosition;
                                gunPoint.localRotation = keyPoint.localRotation;
                                gunPoint.localScale = keyPoint.localScale;
                                hasGun = true;
                            }
                            //找到绑定点位
                            for (int k = 0; k < gpuAnim.mountPoints.Length; k++)
                            {
                                if (gpuAnim.mountPoints[k] == weapon.point.name)
                                {
                                    index = k;
                                    break;
                                }
                            }

                            if (gunPoint)
                            {
                                instancingGroup.specialPointsIndex.Add(index);
                                instancingGroup.specialPoints.Add(gunPoint);
                            }
                        }
                    }

                    if (!hasGun)
                    {
                        BattleDebug.LogError("hasGun is false:" + objName);   
                    }
                }

                if (isSurvivor)
                {
                    newPrefab = rootGameObject;
                }
                PrefabUtility.SaveAsPrefabAsset(newPrefab, prefabPath);
                Object.DestroyImmediate(newPrefab);
            }
            Object.DestroyImmediate(originPrefab);
        }

        private ModelGpuInstancing CreateGpuInstancing(string rendererName,string textureAssetPath,string materialAssetPath,Mesh mesh)
        {
            Texture weaponTexture = null;
            if(!string.IsNullOrEmpty(textureAssetPath))
                weaponTexture = AssetDatabase.LoadAssetAtPath<Texture>(textureAssetPath);
            var material = AssetDatabase.LoadAssetAtPath<Material>(materialAssetPath);
            var shader = Shader.Find("GGame/HRP/MainCity/GPUInstanceRpg");
            var shadowShader = Shader.Find("GGame/HRP/MainCity/GPUInstanceShadowRpg");
            if (material == null)
            {
                BattleDebug.LogError(materialAssetPath);
            }
            GetInstanceMaterial(rendererName, material, weaponTexture, shader,shadowShader,out var instanceMat, out var shadowMat);

            var gameObject = new GameObject(rendererName);
            var modelInstaning = gameObject.AddComponent<ModelGpuInstancing>();
            modelInstaning.mat = instanceMat;
            modelInstaning.shadowMat = shadowMat;
            modelInstaning.mesh = mesh;
            modelInstaning.shadowMesh = mesh;
            modelInstaning.poolKey = mesh.GetHashCode();
            return modelInstaning;
        }
        
        private static readonly int MainLightCol = Shader.PropertyToID("_MainLightCol");
        private static readonly int MainLightPos = Shader.PropertyToID("_MainLightPos");
        private static readonly int SecondLightCol = Shader.PropertyToID("_SecondLightCol");
        private static readonly int SecondLightPos = Shader.PropertyToID("_SecondLightPos");

        private void CopyMaterialProperties(Material rawMaterial, Material targetMaterial)
        {
            var rawMatSerializedObj = new SerializedObject(rawMaterial);
            var properties = rawMatSerializedObj.FindProperty("m_SavedProperties");
            var floats = properties.FindPropertyRelative("m_Floats");
            var colors = properties.FindPropertyRelative("m_Colors");
            CopyFloats(targetMaterial, floats);
            CopyColors(targetMaterial, colors);
        }

        private void CopyColors(Material targetMaterial, SerializedProperty colors)
        {
            if (colors == null || colors.arraySize == 0)
            {
                return;
            }
            for (int i = colors.arraySize - 1; i >= 0; i--)
            {
                var arrayElementAtIndex = colors.GetArrayElementAtIndex(i);
                string propertyName = arrayElementAtIndex.FindPropertyRelative("first").stringValue;
                var colorValue = arrayElementAtIndex.FindPropertyRelative("second").colorValue;
                if (targetMaterial.HasProperty(propertyName))
                {
                    targetMaterial.SetColor(propertyName, colorValue);
                }
            }
        }

        private void CopyFloats(Material targetMaterial, SerializedProperty floats)
        {
            if (floats == null)
            {
                return;
            }
            for (int i = floats.arraySize - 1; i >= 0; i--)
            {
                var arrayElementAtIndex = floats.GetArrayElementAtIndex(i);
                string propertyName = arrayElementAtIndex.FindPropertyRelative("first").stringValue;
                var floatValue = arrayElementAtIndex.FindPropertyRelative("second").floatValue;
                if (targetMaterial.HasProperty(propertyName))
                {
                    targetMaterial.SetFloat(propertyName, floatValue);
                }
            }
        }

        private void CopyTextures(Material targetMaterial, SerializedProperty texEnvs)
        {
            if (texEnvs == null)
            {
                return;
            }
            for (int i = texEnvs.arraySize - 1; i >= 0; i--)
            {
                var arrayElementAtIndex = texEnvs.GetArrayElementAtIndex(i);
                string propertyName = arrayElementAtIndex.FindPropertyRelative("first").stringValue;
                var objectValue = arrayElementAtIndex.FindPropertyRelative("second").objectReferenceValue;
                if (targetMaterial.HasProperty(propertyName))
                {
                    targetMaterial.SetTexture(propertyName, (Texture)objectValue);
                }
            }
        }

        private void GetInstanceMaterial(string srcMatName, Material rawMaterial,Texture mainTexture,Shader shader,Shader shadowShader,  out Material instanceMat, out Material instanceShadowMat,bool dissolve = false)
        {
            string materialPath = materialDirectory + "/mat_" + srcMatName + ".mat";
            string shadowMaterialPath = materialDirectory + "/mat_" + srcMatName + "_shadow.mat";

            if (File.Exists(materialPath))
            {
                instanceMat = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
                instanceMat.shader = shader;
            }
            else
            {
                instanceMat = new Material(shader);
                AssetDatabase.CreateAsset(instanceMat, materialPath);
            }

            CopyMaterialProperties(rawMaterial, instanceMat);

            instanceMat.SetVector(MainLightCol, _mainLightColor);
            instanceMat.SetVector(MainLightPos, _mainLightPos);
            instanceMat.SetVector(SecondLightCol, _secondLightColor);
            instanceMat.SetVector(SecondLightPos, _secondLightPos);
            instanceMat.SetTexture("_MainTex", mainTexture);

            if (isSurvivor)
            {
                var noisePath = "Assets/TA/TA_CypTest/Sea/burn_noise.png";
                var noiseTexture = AssetDatabase.LoadAssetAtPath<Texture>(noisePath);
                instanceMat.SetTexture("_DissolveTex", noiseTexture);
                if (dissolve)
                {
                    var keyword = new LocalKeyword(shader, "_DISSOLVE_ON");
                    instanceMat.SetKeyword(keyword,true);
                }
                else
                {
                    //BattleDebug.LogError("不存在KeyWord" + shader.name);
                }
            }

            instanceMat.enableInstancing = true;
            
            if (File.Exists(shadowMaterialPath))
            {
                instanceShadowMat = AssetDatabase.LoadAssetAtPath<Material>(shadowMaterialPath);
            }
            else
            {
                instanceShadowMat = new Material(shadowShader);
                AssetDatabase.CreateAsset(instanceShadowMat, shadowMaterialPath);
            }

            instanceShadowMat.SetVector("_ShadowOffset", new Vector4(-0.57f, 0, 0, 0));
            instanceShadowMat.shader = shadowShader;
            instanceShadowMat.enableInstancing = true;
        }

        /// <summary>
        /// 给所有的Bone一个Index
        /// </summary>
        /// <param name="target"></param>
        /// <param name="validSkinnedMeshRenderers"></param>
        /// <returns></returns>
        private Transform[] CollectBones(GameObject target, List<SkinnedMeshRenderer> validSkinnedMeshRenderers)
        {
            if (validSkinnedMeshRenderers.Count == 0)
            {
                UnityEngine.Debug.LogError("Don't has SkinnedMeshRenderer component  " + target.name);
                return null;
            }

            var rootBone = validSkinnedMeshRenderers[0].rootBone;
            if (rootBone == null)
            {
                UnityEngine.Debug.LogError("Don't has Root: " + target.name);
                return null;
            }
            var boneTransformList = new Transform[boneNameList.Count];
            foreach (var skinnedMeshRenderer in validSkinnedMeshRenderers)
            {
                if (rootBone != skinnedMeshRenderer.rootBone)
                {
                    UnityEngine.Debug.LogError(skinnedMeshRenderer.name + ":Root bone error:" + skinnedMeshRenderer.rootBone.name);
                    continue;
                }

                var bones = skinnedMeshRenderer.bones;
                foreach (var bone in bones)
                {
                    string boneName = bone.name;

                    // 不要包含手指和脚趾
                    if (IsIgnoredBone(boneName))
                        continue;

                    if (boneMap.TryGetValue(boneName, out int boneIndex))
                    {
                        if (boneTransformList[boneIndex] == null)
                            boneTransformList[boneIndex] = bone;
                        else if(boneTransformList[boneIndex] != bone)
                            UnityEngine.Debug.LogErrorFormat("发现不同的骨骼节点: {0}, {1}", boneTransformList[boneIndex], bone);
                    }
                    else
                    {
                        UnityEngine.Debug.LogErrorFormat("不存在的骨骼: {0}", boneName);
                    }
                }
            }
            var weapon = target.GetComponent<GpuSkinningWeapon>();
            if (weapon != null)
            {
                for (int i = 0; i < weapon.weaponList.Count; i++)
                {
                    var point = weapon.weaponList[i].point;
                    var boneName = point.name;
                    if (boneMap.TryGetValue(boneName, out int boneIndex))
                    {
                        if (boneTransformList[boneIndex] == null)
                            boneTransformList[boneIndex] = point;
                        else if(boneTransformList[boneIndex] != point)
                            UnityEngine.Debug.LogErrorFormat("发现不同的骨骼节点: {0}, {1}", boneTransformList[boneIndex], point);
                    }
                    else
                    {
                        UnityEngine.Debug.LogErrorFormat("不存在的骨骼: {0}", boneName);
                    }
                }
            }

            /*if (weaponBone != null)
            {
                var boneName = weaponBone.name;
                if (boneMap.TryGetValue(boneName, out int boneIndex))
                {
                    if (boneTransformList[boneIndex] == null)
                        boneTransformList[boneIndex] = weaponBone;
                    else if(boneTransformList[boneIndex] != weaponBone)
                        UnityEngine.Debug.LogErrorFormat("发现不同的骨骼节点: {0}, {1}", boneTransformList[boneIndex], weaponBone);
                }
                else
                {
                    UnityEngine.Debug.LogErrorFormat("不存在的骨骼: {0}", boneName);
                }
            }*/

            for (var i = 0; i < boneTransformList.Length; i++)
            {
                if (boneTransformList[i] == null)
                    boneTransformList[i] = rootBone;
            }

            return boneTransformList;
        }

        private static StringBuilder stringBuilder = new StringBuilder();
        private Vector4 _mainLightColor = new Vector4(1.5f, 1.382f, 1.358f, 1);
        private Vector4 _mainLightPos = new Vector4(-0.74f, -0.6f, 0.276f, 0f);
        private Vector4 _secondLightColor = new Vector4(0.096f, 0.099f, 0.1f, 1);
        private Vector4 _secondLightPos = new Vector4(-0.1227f, -0.707f, 0.696f, 0);

        /// <summary>
        /// 主要作用： 将targetMesh的 顶点、法线根据骨骼影响转换，并存储；将骨骼、权重存到UV1上
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="skinnedMeshRenderer"></param>
        /// <param name="targetMesh"></param>
        /// <param name="boneTransformList"></param>
        private void RebuildMeshBindPose(GameObject obj, SkinnedMeshRenderer skinnedMeshRenderer, Mesh targetMesh, Transform[] boneTransformList)
        {
            // Init Bones
            var aBones = skinnedMeshRenderer.bones;
            int numBones = aBones.Length;

            int[] remapIdx = new int[numBones];
            for (int i = 0; i < numBones; ++i)
                remapIdx[i] = i;

            for (int i = 0; i < numBones; ++i)
            {
                var bone = aBones[i];
                var boneName = bone.name;
                if (boneMap.TryGetValue(boneName, out int boneIndex))
                {
                    remapIdx[i] = boneIndex;
                }
                else
                {
                    remapIdx[i] = -1;
                    if (IsIgnoredBone(boneName))
                    {
                        
                    }
                    else
                    {
                        UnityEngine.Debug.LogErrorFormat("没有找到骨骼: {0}", boneName);
                    }
                }
            }

            var aNormals = targetMesh.normals;
            var aVertices = targetMesh.vertices;
            var uv2 = targetMesh.uv2;
            // 骨骼信息存到uv1 (只用2根骨骼）
            var uv1 = new List<Vector4>();

            var aBindPoses = targetMesh.bindposes;
            var aBoneWeights = targetMesh.boneWeights;
            stringBuilder.AppendFormat("开始处理网格: {0}\n{1}\n", targetMesh, AssetDatabase.GetAssetPath(targetMesh));
            //将顶点、法线转到世界空间下；存储骨骼、权重信息到UV1
            for (int i = 0; i < targetMesh.vertexCount; ++i)
            {
                var boneWeight1 = Vector4.zero;
                var boneWeight2 = Vector4.zero;
                var v3Normal = Vector3.zero;
                var v3World = Vector3.zero;
                var bw = aBoneWeights[i];

                int boneCount = 0;
                if (Mathf.Abs(bw.weight0) > 0.00001f)
                {
                    var bone = (aBones == null || aBones.Length == 0) ? obj.transform : aBones[bw.boneIndex0].transform;
                    v3World = (bone.localToWorldMatrix * aBindPoses[bw.boneIndex0]).MultiplyPoint3x4(aVertices[i]) * bw.weight0;
                    v3Normal = (bone.localToWorldMatrix * aBindPoses[bw.boneIndex0]).MultiplyVector(aNormals[i]) * bw.weight0;
                    boneWeight1.x = remapIdx[bw.boneIndex0]; //哪根骨骼
                    boneWeight1.y = bw.weight0;//权重多少

                    if (remapIdx[bw.boneIndex0] == -1)
                        boneWeight1.x = 1;

                    boneCount++;
                }
                else
                {
                    Debug.LogWarning(targetMesh + " Idx:" + i + ": Bone 0 weight == 0.0f.");
                }

                if (Mathf.Abs(bw.weight1) > 0.00001f)
                {
                    var bone = (aBones == null || aBones.Length == 0) ? obj.transform : aBones[bw.boneIndex1].transform;
                    v3World += (bone.localToWorldMatrix * aBindPoses[bw.boneIndex1]).MultiplyPoint3x4(aVertices[i]) * bw.weight1;
                    v3Normal += (bone.localToWorldMatrix * aBindPoses[bw.boneIndex1]).MultiplyVector(aNormals[i]) * bw.weight1;

                    boneWeight1.z = remapIdx[bw.boneIndex1];
                    boneWeight1.w = bw.weight1;
                    boneCount++;

                    if (remapIdx[bw.boneIndex1] == -1)
                    {
                        boneWeight1.z = 1;
                        boneWeight1.w = 0;
                    }
                }

                
                if (Mathf.Abs(bw.weight2) > 0.00001f)
                {
                    Transform bone = (aBones == null || aBones.Length == 0) ? obj.transform : aBones[bw.boneIndex2].transform;
                    v3World += (bone.localToWorldMatrix * aBindPoses[bw.boneIndex2]).MultiplyPoint3x4(aVertices[i]) * bw.weight2;
                    v3Normal += (bone.localToWorldMatrix * aBindPoses[bw.boneIndex2]).MultiplyVector(aNormals[i]) * bw.weight2;

                    boneWeight2.x = remapIdx[bw.boneIndex2];
                    boneWeight2.y = bw.weight2;
                    boneCount++;
                }

                if (Mathf.Abs(bw.weight3) > 0.00001f)
                {
                    Transform bone = (aBones == null || aBones.Length == 0) ? obj.transform : aBones[bw.boneIndex3].transform;
                    v3World += (bone.localToWorldMatrix * aBindPoses[bw.boneIndex3]).MultiplyPoint3x4(aVertices[i]) * bw.weight3;
                    v3Normal += (bone.localToWorldMatrix * aBindPoses[bw.boneIndex3]).MultiplyVector(aNormals[i]) * bw.weight3;

                    boneWeight2.z = remapIdx[bw.boneIndex3];
                    boneWeight2.w = bw.weight3;
                    boneCount++;
                }

                if (boneCount > 2)
                {
                    stringBuilder.AppendFormat("顶点[{0}] 用到了{1}根骨骼\n", i, boneCount);
                }

                //boneWeight1.y = 1 - boneWeight1.w - boneWeight2.y - boneWeight2.w;
                boneWeight1.y = 1 - boneWeight1.w;

                aVertices[i] = v3World;
                aNormals[i] = v3Normal;
                uv1.Add(boneWeight1);
                //uv2.Add(boneWeight2);
            }

            for (int bindPoseIndex = 0; bindPoseIndex < aBindPoses.Length; bindPoseIndex++)
            {
                aBindPoses[bindPoseIndex] = aBones[bindPoseIndex].worldToLocalMatrix * obj.transform.localToWorldMatrix;
            }

            targetMesh.vertices = aVertices;
            targetMesh.normals = aNormals;
            targetMesh.bindposes = null;
            // 骨骼信息存在UV1
            targetMesh.SetUVs(1, uv1);
            // 原来的UV1保存到UV2
            targetMesh.SetUVs(2, uv2);
        }

        public int GetMotionIndex(AnimationClip clip)
        {
            if (animationClipMap.TryGetValue(clip, out int motionIndex))
            {
                return motionIndex;
            }

            if (animationOverrideClipMap.TryGetValue(clip, out int overrideMotionIndex))
            {
                return overrideMotionIndex;
            }
            return -1;
        }

        public Motion GetOverrideMotion(AnimationClip clip)
        {
            if (animationOverrideClipMap.TryGetValue(clip, out int overrideMotionIndex))
            {
                return animationClipList[overrideMotionIndex];
            }
            return null;
        }
    }
}
